package top.jfunc.common.thread.monitor.adapter;

import top.jfunc.common.thread.monitor.CountedRejectedExecutionHandler;
import top.jfunc.common.thread.monitor.StatisticsRunnable;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 统计任务执行情况
 */
public class MonitoredStatisticsCounter {
    /**
     * Task execute timeout, unit (ms), just for statistics.
     * 设置了该值，才会统计执行时间
     */
    private long executeTimeout = 0;

    /**
     * Task queue wait timeout, unit (ms), just for statistics.
     * 设置了该值，才会统计排队时间
     */
    private long queueTimeout = 0;
    /**
     * 记录总执行时间
     */
    private long totalExecuteTime = 0;
    /**
     * 记录一个统计周期内执行的最大时间
     */
    private long maxExecuteTime = 0;
    /**
     * 记录一个统计周期内执行的最小时间
     */
    private long minExecuteTime = 0;
    /**
     * Count run timeout tasks.
     */
    private int executeTimeoutCount = 0;
    /**
     * Count run with Exception tasks.
     */
    private int executeExceptionCount = 0;

    /**
     * 记录一个统计周期内排队的最大时间
     */
    private long minQueueTime = 0;
    /**
     * 记录一个统计周期内排队的最大时间
     */
    private long maxQueueTime = 0;
    /**
     * Count queue wait timeout tasks.
     */
    private int queueTimeoutCount = 0;

    private CountedRejectedExecutionHandler rejectedExecutionHandler;

    //并发一致性锁
    private final Lock lock = new ReentrantLock();

    public void beforeExecute(Thread t, Runnable r) {
        if (!(r instanceof StatisticsRunnable)) {
            return;
        }
        lock.lock();
        try {
            StatisticsRunnable runnable = (StatisticsRunnable) r;
            long currTime = System.currentTimeMillis();
            if (executeTimeout > 0) {
                runnable.setStartTime(currTime);
            }
            if (queueTimeout > 0) {
                long queueTime = currTime - runnable.getSubmitTime();

                //更新最大等待时间
                if(queueTime > maxQueueTime){
                    maxQueueTime = queueTime;
                }

                //更新最小等待时间
                if(queueTime < minQueueTime || minQueueTime == 0){
                    minQueueTime = queueTime;
                }

                //看是否超时
                if (queueTime > queueTimeout) {
                    queueTimeoutCount++;
                    //其他的可以做诸如告警
                }
            }
        }finally {
            lock.unlock();
        }
    }
    public void afterExecute(Runnable r, Throwable t) {
        if (!(r instanceof StatisticsRunnable)) {
            return;
        }

        lock.lock();
        try {
            if (executeTimeout > 0) {
                StatisticsRunnable runnable = (StatisticsRunnable) r;
                long executeTime = System.currentTimeMillis() - runnable.getStartTime();
                //统计执行时间
                totalExecuteTime += (executeTime);

                //更新最大执行时间
                if(executeTime > maxExecuteTime){
                    maxExecuteTime = executeTime;
                }
                //更新最小执行时间
                if(executeTime < minExecuteTime || minExecuteTime == 0){
                    minExecuteTime = executeTime;
                }

                //看是否超时
                if (executeTime > executeTimeout) {
                    executeTimeoutCount++;
                    //其他的可以做诸如告警
                }

            }

            //发生异常统计
            if (null != t) {
                executeExceptionCount++;
                //其他的可以做诸如告警
            }
        }finally {
            lock.unlock();
        }
    }

    public long getTotalExecuteTime() {
        return this.totalExecuteTime;
    }

    public long getMaxExecuteTime() {
        return this.maxExecuteTime;
    }

    public long getMinExecuteTime() {
        return this.minExecuteTime;
    }

    public int getExecuteTimeoutCount() {
        return this.executeTimeoutCount;
    }

    public int getExecuteExceptionCount() {
        return executeExceptionCount;
    }

    public long getMinQueueTime() {
        return this.minQueueTime;
    }

    public long getMaxQueueTime() {
        return this.maxQueueTime;
    }

    public int getQueueTimeoutCount() {
        return this.queueTimeoutCount;
    }

    public int getRejectedCount(){
        return null != rejectedExecutionHandler ? rejectedExecutionHandler.getRejectedCount() : 0;
    }

    public void resetStatistic() {
        lock.lock();
        try {
            //totalExecuteTime.set(0);
            maxExecuteTime = 0;
            minExecuteTime = 0;
            executeTimeoutCount = 0;
            executeExceptionCount = 0;

            maxQueueTime = 0;
            minQueueTime = 0;
            queueTimeoutCount = 0;

            if(null != rejectedExecutionHandler){
                rejectedExecutionHandler.reset();
            }
        }finally {
            lock.unlock();
        }

    }

    public MonitoredStatisticsCounter setExecuteTimeout(long executeTimeout) {
        this.executeTimeout = executeTimeout;
        return this;
    }

    public long getExecuteTimeout() {
        return this.executeTimeout;
    }

    public MonitoredStatisticsCounter setQueueTimeout(long queueTimeout) {
        this.queueTimeout = queueTimeout;
        return this;
    }

    public long getQueueTimeout() {
        return this.queueTimeout;
    }
    public CountedRejectedExecutionHandler getRejectedExecutionHandler() {
        return rejectedExecutionHandler;
    }

    public MonitoredStatisticsCounter setRejectedExecutionHandler(CountedRejectedExecutionHandler rejectedExecutionHandler) {
        this.rejectedExecutionHandler = rejectedExecutionHandler;
        return this;
    }
}
